import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEnum, IsInt, IsNotEmpty, IsNumberString, IsOptional, IsString, Min } from 'class-validator';
import { Direction } from "common/utils/data";
import { SelectQueryBuilder } from "typeorm";
import { OrderByCondition } from "typeorm/find-options/OrderByCondition";
import dayjs from "dayjs";

const MAX_PAGE_SIZE = 100;

export abstract class BasePageDto {
  @IsOptional()
  @ApiPropertyOptional({
    description: '当前页码，不传为1',
    default: 1,
  })
  @IsInt()
  @Min(1)
  private pageIndex?: number;

  @IsOptional()
  @ApiPropertyOptional({
    description: `分页数量，不传为10,最大${MAX_PAGE_SIZE}`,
    default: 10,
  })
  @IsInt()
  @Min(1)
  private pageSize?: number;

  private getPageIndex() {
    return this.pageIndex ? this.pageIndex : 1;
  }

  getPageSize(allowMax = false) {
    const maxPageSize = allowMax ? 9999 : MAX_PAGE_SIZE;
    let pageSize = this.pageSize ? this.pageSize : 10;
    pageSize = pageSize > maxPageSize || pageSize < 0 ? maxPageSize : pageSize;
    return pageSize;
  }

  getOffset(allowMax = false) {
    return (this.getPageIndex() - 1) * this.getPageSize(allowMax);
  }
}

export abstract class BaseScrollPageDto {
  @IsNotEmpty()
  @ApiProperty({
    description: '排序字段',
    example: 'createTime',
  })
  @IsString()
  orderBy: string;

  @IsNotEmpty()
  @ApiProperty({
    description: '排序方向',
    example: 'DESC',
  })
  @IsEnum(Direction)
  direction: Direction;

  @IsOptional()
  @ApiPropertyOptional({
    description: '上一页最后一条数据的排序字段值',
    example: '2025-01-01 00:00:00',
  })
  @IsNumberString()
  lastKey?: string;

  @IsOptional()
  @ApiPropertyOptional({
    description: '上一页最后一条数据的id',
    example: '1',
  })
  @IsNumberString()
  lastId?: string;

  @IsOptional()
  @ApiPropertyOptional({
    description: `分页数量，不传为20,最大${MAX_PAGE_SIZE}`,
    default: 20,
  })
  @IsInt()
  @Min(1)
  private pageSize?: number;

  getPageSize(allowMax = false) {
    const maxPageSize = allowMax ? 9999 : MAX_PAGE_SIZE;
    let pageSize = this.pageSize ? this.pageSize : 10;
    pageSize = pageSize > maxPageSize || pageSize < 0 ? maxPageSize : pageSize;
    return pageSize;
  }

  getLastKey(){
    return this.lastKey ? Number(this.lastKey) : undefined;
  }

  getLastId(){
    return this.lastId ? Number(this.lastId) : undefined;
  }

  wrapQueryBuilder(
    qb: SelectQueryBuilder<any>,
    modelName: string,
    param?: {
      firstOrderBy?: (origin: OrderByCondition)=>OrderByCondition,
    }
  ){
    const orderByField = this.orderBy.includes('.') ? this.orderBy : `${modelName}.${this.orderBy}`;

    qb.addSelect([
      `${modelName}.id`,
    ])

    const sourceOrderBy: OrderByCondition = {
      [orderByField]: this.direction,
      [`${modelName}.id`]: 'DESC',
    };

    qb.take(this.getPageSize())

    if (this.getLastKey() && this.getLastId()) {
      qb.andWhere(
        `(
        (${orderByField} ${this.direction === Direction.DESC ? '<' : '>'} :lastKey) OR 
        (${orderByField} = :lastKey AND ${modelName}.id < :lastId)
        )`
        , {
        lastKey: ['createTime', 'updateTime'].includes(this.orderBy) ? dayjs(this.getLastKey()).format('YYYY-MM-DD HH:mm:ss.SSS') : this.getLastKey(),
        lastId: this.getLastId(),
      });
      qb.orderBy(sourceOrderBy)
    }else{
      qb.orderBy(param?.firstOrderBy ? param.firstOrderBy(sourceOrderBy) : sourceOrderBy)
    }
  }
}

export abstract class BaseTencentCaptchaDto {
  @IsNotEmpty()
  @ApiProperty()
  @IsString()
  ticket: string;

  @IsNotEmpty()
  @ApiProperty()
  @IsString()
  randstr: string;
}

export abstract class BaseVcodeDto {
  @IsOptional()
  @ApiPropertyOptional()
  @IsString()
  vcodeKey?: string;

  @IsNotEmpty()
  @ApiProperty()
  @IsString()
  vcode: string;
}

export abstract class BaseOffsetDto {
  @IsOptional()
  @ApiPropertyOptional({
    description: '偏移量，是排序字段',
  })
  @IsInt()
  @Min(0)
  offset?: number;
}
